package tech.blueglacier.util;

import org.apache.james.mime4j.dom.field.FieldName;
import org.apache.james.mime4j.dom.field.ParsedField;
import org.apache.james.mime4j.message.MaximalBodyDescriptor;
import org.apache.james.mime4j.stream.BodyDescriptor;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * @author zhangxinchen
 * @since 2020/7/15
 */
public class EncodedWordUtil {

    private static final Pattern PATTERN_CONTAINS_ENCODED_WORD = Pattern.compile("(.*?)=\\?(.+?)\\?(\\w)\\?(.*?)\\?=(.*?)", Pattern.DOTALL);
    private static final String PATTERN_APPLE_MAIL_ATTACHMENT_FILENAME_ENCODED_WORD = "filename*";
    private static final String CONTENT_TYPE_KEY = FieldName.CONTENT_TYPE.toLowerCase(Locale.US);
    private static final String CONTENT_DISPOSITION_KEY = FieldName.CONTENT_DISPOSITION.toLowerCase(Locale.US);
    private static final String BODY_DESCRIPTOR_RAW_FIELD = "fields";
    private static final String LINE_FEED = "\r?\n";
    private static final String TAB = "\t";

    private static Field reflectedField;

    static {
        try {
            reflectedField = MaximalBodyDescriptor.class.getDeclaredField(BODY_DESCRIPTOR_RAW_FIELD);
            reflectedField.setAccessible(true);
        } catch (NoSuchFieldException ignore) {
            // it's impossible
        }
    }

    @SuppressWarnings("unchecked")
    public static boolean attachmentNameContainsEncodedWords(BodyDescriptor bd) {
        if (!(bd instanceof MaximalBodyDescriptor)) {
            return false;
        }

        try {
            Map<String, ParsedField> parameter = (Map<String, ParsedField>) reflectedField.get(bd);
            return attachmentNameFromContentDispositionContainsEncodedWords(parameter)
                    || attachmentNameFromContentTypeContainsEncodedWords(parameter);
        } catch (Exception e) {
            return false;
        }
    }

    private static boolean attachmentNameFromContentDispositionContainsEncodedWords(Map<String, ParsedField> parameter) {
        if (parameter.containsKey(CONTENT_DISPOSITION_KEY)) {
            String contentDisposition = new String(parameter.get(CONTENT_DISPOSITION_KEY).getRaw().toByteArray(), StandardCharsets.UTF_8);
            return maybeContainsEncodedWords(contentDisposition);
        }

        return false;
    }


    private static boolean attachmentNameFromContentTypeContainsEncodedWords(Map<String, ParsedField> parameter) {
        if (parameter.containsKey(CONTENT_TYPE_KEY)) {
            String contentType = new String(parameter.get(CONTENT_TYPE_KEY).getRaw().toByteArray(), StandardCharsets.UTF_8)
                    .replaceAll(LINE_FEED, "")
                    .replaceAll(TAB, "");
            return maybeContainsEncodedWords(contentType);
        }

        return false;
    }

    public static boolean maybeContainsEncodedWords(String input) {
        return containsStandardEncodedWords(input) || containsAppleMailEncodedWord(input);
    }

    public static boolean containsStandardEncodedWords(String input) {
        return PATTERN_CONTAINS_ENCODED_WORD.matcher(input).matches();
    }

    public static boolean containsAppleMailEncodedWord(String input) {
        return input.contains(PATTERN_APPLE_MAIL_ATTACHMENT_FILENAME_ENCODED_WORD);
    }
}
